import socket
import threading
import time
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox

from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.util.rogercam_au_test import construct_protocol
from app.view.base_view import View


@annotate(ClassInfo(name="FtAuCardCmdConfig", category="ft_tool"))
class FtAuCardCmdConfigView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 服务器状态
        self.server_running = False
        self.server_socket = None
        self.clients = {}  # {client_address: (socket, device_id)}
        self.selected_client = None

        # 设备ID映射
        self.tcp_device_ids = {}  # {client_address: device_id}
        self.device_id_var = tk.StringVar(value="")
        self.device_ids = []
        self.log_line_clear = tk.StringVar(value="1000")  # log自动清除

        # IP过滤变量
        self.ip_filter_var = tk.StringVar()
        self.ip_filter_var.trace_add("write", self.on_ip_filter_var_change)
        # Log过滤变量
        self.log_filter_var = tk.StringVar()

        # 创建UI
        self._create_widgets()

        # 服务器线程
        self.server_thread = None

        # 窗口关闭事件绑定
        # self.protocol("WM_DELETE_WINDOW", self.on_close)

        # 添加默认命令行
        self.add_default_commands()
        self.to_find_device_id = False

    def _create_widgets(self):
        # 顶部控制区域
        top_frame = ttk.LabelFrame(self, text="服务器控制", padding=10)
        top_frame.pack(fill=X, padx=10, pady=5)

        # 端口设置
        port_frame = ttk.Frame(top_frame)
        port_frame.pack(side=LEFT, padx=10)
        ttk.Label(port_frame, text="端口:").pack(side=LEFT, padx=5)
        self.port_entry = ttk.Entry(port_frame, width=10)
        self.port_entry.pack(side=LEFT, padx=5)
        self.port_entry.insert(0, "14121")

        # 设备ID显示和修改
        device_frame = ttk.Frame(top_frame)
        device_frame.pack(side=LEFT, padx=10)
        ttk.Label(device_frame, text="设备ID:").pack(side=LEFT, padx=5)
        self.device_id_combobox = ttk.Combobox(
            device_frame,
            textvariable=self.device_id_var,
            state="readonly",
            width=15
        )
        self.device_id_combobox.pack(side=LEFT, padx=5)

        self.update_id_btn = ttk.Button(device_frame, text="获取ID", command=self.update_device_id)
        self.update_id_btn.pack(side=LEFT, padx=5)
        self.update_id_btn = ttk.Button(device_frame, text="清除ID", command=self.clear_device_id)
        self.update_id_btn.pack(side=LEFT, padx=5)

        # log清除
        line_clear_frame = ttk.Frame(top_frame)
        line_clear_frame.pack(side=LEFT, padx=10)
        ttk.Label(line_clear_frame, text="Log行数:").pack(side=LEFT, padx=5)

        self.line_clear_entry = ttk.Entry(line_clear_frame, textvariable=self.log_line_clear, width=20)
        self.line_clear_entry.pack(side=LEFT, padx=5)

        self.start_btn = ttk.Button(line_clear_frame, text="清除Log", command=self.log_clear, bootstyle=SUCCESS)
        self.start_btn.pack(side=LEFT, padx=5)

        # 服务器控制按钮
        control_frame = ttk.Frame(top_frame)
        control_frame.pack(side=RIGHT, padx=10)
        self.start_btn = ttk.Button(control_frame, text="启动服务", command=self.start_server, bootstyle=SUCCESS)
        self.start_btn.pack(side=LEFT, padx=5)

        self.stop_btn = ttk.Button(control_frame, text="停止服务", command=self.stop_server, bootstyle=DANGER)
        self.stop_btn.pack(side=LEFT, padx=5)
        self.stop_btn.config(state=DISABLED)

        # 主内容区域
        main_frame = ttk.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=True)

        # 左侧命令区域
        left_frame = ttk.LabelFrame(main_frame, text="命令管理", padding=10)
        left_frame.pack(side=LEFT, fill=BOTH, expand=True, padx=(0, 10))

        # 命令管理按钮区域
        cmd_line_frame = ttk.Frame(left_frame)
        cmd_line_frame.pack(fill=X, pady=10)
        self.hex_entry = ttk.Entry(cmd_line_frame, width=80)
        self.hex_entry.pack(side=LEFT, padx=5)

        ttk.Button(cmd_line_frame, text="发送HEX", command=self.send_hex_line,
                   bootstyle=INFO).pack(side=RIGHT,
                                        padx=5)

        # 命令管理按钮区域
        cmd_manage_frame = ttk.Frame(left_frame)
        cmd_manage_frame.pack(fill=X, pady=10)

        ttk.Button(cmd_manage_frame, text="添加命令", command=lambda check=False: self.add_command_row(check=check),
                   bootstyle=INFO).pack(side=LEFT,
                                        padx=5)
        ttk.Button(cmd_manage_frame, text="删除选中", command=self.remove_selected_row, bootstyle=WARNING).pack(
            side=LEFT, padx=5)
        ttk.Button(cmd_manage_frame, text="清空所有", command=self.clear_all_commands, bootstyle=DANGER).pack(
            side=LEFT, padx=5)
        ttk.Button(cmd_manage_frame, text="默认参数", command=self.default_commands, bootstyle=INFO).pack(
            side=LEFT, padx=5)

        # 命令区域标题行
        header_frame = ttk.Frame(left_frame)
        header_frame.pack(fill=X, pady=(0, 5))

        ttk.Label(header_frame, text="不换行", width=10).grid(row=0, column=0, padx=5)
        ttk.Label(header_frame, text="命令名称", width=15).grid(row=0, column=1, padx=5)
        ttk.Label(header_frame, text="命令内容", width=50).grid(row=0, column=2, padx=5)
        ttk.Label(header_frame, text="操作", width=10).grid(row=0, column=3, padx=5)

        # 创建带滚动条的命令区域
        self.cmd_canvas = tk.Canvas(left_frame)
        self.cmd_scrollbar = ttk.Scrollbar(left_frame, orient="vertical", command=self.cmd_canvas.yview)
        self.cmd_scrollable_frame = ttk.Frame(self.cmd_canvas)

        # 绑定滚动事件
        self.cmd_scrollable_frame.bind(
            "<Configure>",
            lambda e: self.cmd_canvas.configure(
                scrollregion=self.cmd_canvas.bbox("all")
            )
        )

        self.cmd_canvas.create_window((0, 0), window=self.cmd_scrollable_frame, anchor="nw")
        self.cmd_canvas.configure(yscrollcommand=self.cmd_scrollbar.set)

        # 布局滚动区域
        self.cmd_canvas.pack(side="left", fill=BOTH, expand=True)
        self.cmd_scrollbar.pack(side="right", fill="y")

        # 命令行容器
        self.commands_frame = ttk.Frame(self.cmd_scrollable_frame)
        self.commands_frame.pack(fill=X, pady=5)

        # 命令行列表 - 每个元素是 (frame, name_entry, cmd_entry, send_btn, select_btn, select_var)
        self.command_entries = []
        self.selected_row = None  # 跟踪选中的行

        # 右侧信息区域
        right_frame = ttk.Frame(main_frame)
        right_frame.pack(side=RIGHT, fill=BOTH, expand=True)

        # 客户端列表
        client_frame = ttk.LabelFrame(right_frame, text="连接的客户端", padding=10)
        client_frame.pack(fill=BOTH, expand=True, pady=(0, 10))

        # IP搜索栏
        ip_filter_frame = ttk.Frame(client_frame)
        # MAC地址输入
        ttk.Label(ip_filter_frame, text="IP过滤:").pack(side=LEFT)
        ttk.Entry(ip_filter_frame, textvariable=self.ip_filter_var, width=100).pack(side=RIGHT, fill=X)
        ip_filter_frame.pack(side=TOP, fill=X, pady=5)

        self.client_listbox = tk.Listbox(client_frame, selectmode=tk.SINGLE)
        self.client_listbox.pack(side=LEFT, fill=BOTH, expand=True)
        self.client_listbox.bind('<<ListboxSelect>>', self.on_client_select)

        scrollbar = ttk.Scrollbar(client_frame, orient=VERTICAL, command=self.client_listbox.yview)
        scrollbar.pack(side=RIGHT, fill=Y)
        self.client_listbox.config(yscrollcommand=scrollbar.set)

        # 日志区域
        log_frame = ttk.LabelFrame(right_frame, text="通信日志", padding=10)
        log_frame.pack(fill=BOTH, expand=True)

        # Log搜索栏
        log_filter_frame = ttk.Frame(log_frame)
        # MAC地址输入
        ttk.Label(log_filter_frame, text="Log过滤:").pack(side=LEFT)
        ttk.Entry(log_filter_frame, textvariable=self.log_filter_var, width=100).pack(side=RIGHT, fill=X)
        log_filter_frame.pack(side=TOP, fill=X, pady=5)

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, state=DISABLED)
        self.log_text.pack(fill=BOTH, expand=True)

    def on_ip_filter_var_change(self, *args):
        # 过滤IP列表
        self.client_listbox.delete(0, END)
        filter = self.ip_filter_var.get()
        if len(filter) > 0:
            for ip_port in self.clients:
                if filter in ip_port:
                    self.client_listbox.insert(END, ip_port)
        else:
            for ip_port in self.clients:
                self.client_listbox.insert(END, ip_port)

    def save_config(self):
        pass

    def load_config(self):
        pass

    def send_hex_line(self):
        data = self.hex_entry.get().strip()
        hex_bytes = bytes.fromhex(data)
        try:
            client_socket, _ = self.clients[self.selected_client]
            if not client_socket:
                self.log(f"请选择需要发送的IP")
                return
            client_socket.sendall(hex_bytes)
            self.log(f"发送: {self.selected_client}: {hex_bytes.hex().upper()}")
        except Exception as e:
            self.log(f"发送命令失败: {str(e)}")

    def add_command_row(self, check, name_text="", command_text=""):
        """添加一行命令名称、命令输入和发送按钮"""
        frame = ttk.Frame(self.commands_frame)
        frame.pack(fill=X, pady=2)

        # 选择按钮（用于标记要删除的行）
        select_var = tk.BooleanVar(value=check)
        select_btn = ttk.Checkbutton(frame, variable=select_var,
                                     command=lambda f=frame, v=select_var: self.on_row_select(f, v))
        select_btn.pack(side=LEFT, padx=5)

        # 命令名称输入框
        name_entry = ttk.Entry(frame, width=15)
        name_entry.pack(side=LEFT, padx=5)
        if name_text:
            name_entry.insert(0, name_text)

        # 命令内容输入框
        cmd_entry = ttk.Entry(frame, width=45)
        cmd_entry.pack(side=LEFT, fill=X, expand=True, padx=5)
        if command_text:
            cmd_entry.insert(0, command_text)

        # 发送按钮
        send_btn = ttk.Button(frame, text="发送",
                              command=lambda e=cmd_entry, n=name_entry, s=select_var.get(): self.send_command(e, n, s),
                              bootstyle=PRIMARY)
        send_btn.pack(side=RIGHT, padx=5)

        self.command_entries.append((frame, name_entry, cmd_entry, send_btn, select_btn, select_var))

        # 更新滚动区域
        self.cmd_canvas.configure(scrollregion=self.cmd_canvas.bbox("all"))

    def add_default_commands(self):
        self.add_command_row(True, "CMD Mode", "+++")
        self.add_command_row(False, "AT EXITCMD", "AT+EXITCMD")
        self.add_command_row(False, "AT Help", "AT+CMD?")
        self.add_command_row(False, "AT RESET", "AT+RESET")
        self.add_command_row(False, "AT LINK", "AT+LINK?")
        self.add_command_row(False, "AT VERSION", "AT+VERSION?")
        self.add_command_row(False, "AT SCAN", "AT+SCAN")
        self.add_command_row(False, "AT ROLE", "AT+ROLE?")
        self.add_command_row(False, "AT READBINDMAC", "AT+READBINDMAC?")
        self.add_command_row(False, "AT WRITEBINDMAC",
                             "AT+WRITEBINDMAC=D221DAB4CE1C,00000000000F,081920114A4B,000000000000,000000000000,000000000000,000000000000,000000000000")
        self.add_command_row(False, "SEND FILTER", "AT+SEND=AT+FILTER=?,254")
        self.add_command_row(False, "SEND FILTER", "AT+SEND=AT+FILTER=8,0,0,0,00000000050931323234,0,0,0,0")
        self.add_command_row(False, "BeeLinker", "AT+SEND=AT+FILTER=9,0,0,0,00004265654C696E6B6572,0,0,0,0")
        self.add_command_row(False, "LJBW", "AT+SEND=AT+FILTER=8,0,0,0000000000000000004C4A4257,0,0,0,0,0")
        self.add_command_row(False, "SEND FILDEL", "AT+SEND=AT+FILDEL=254")
        self.add_command_row(False, "SEND FILDEL", "AT+SEND=AT+FILDEL=253")
        self.add_command_row(False, "SEND FILTER", "AT+SEND=AT+FILTER=?")
        self.add_command_row(False, "SEND FILTER", "AT+SEND=AT+FILTER=?,0")
        self.add_command_row(False, "SEND FILTER", "AT+SEND=AT+FILTER=?,1")
        self.add_command_row(False, "AT CONN", "AT+CONNECT=4B4A11201908")
        self.add_command_row(False, "AT DISCONN", "AT+DISCONN")

    def on_row_select(self, frame, var):
        """处理行选择事件"""
        # 取消其他行的选择
        if var.get():
            for _, _, _, _, _, v in self.command_entries:
                if v != var:
                    v.set(False)
            self.selected_row = frame
        else:
            self.selected_row = None

    def remove_selected_row(self):
        """删除选中的命令行"""
        if len(self.command_entries) <= 1:
            self.log("至少保留一行命令")
            return

        if self.selected_row:
            for i, (frame, _, _, _, _, var) in enumerate(self.command_entries):
                if frame == self.selected_row:
                    frame.destroy()
                    del self.command_entries[i]
                    self.selected_row = None
                    self.log("已删除选中的命令行")
                    break
        else:
            # 如果没有选中行，删除最后一行
            if len(self.command_entries) > 1:
                frame, _, _, _, _, _ = self.command_entries.pop()
                frame.destroy()
                self.log("已删除最后一行命令")
        # 更新滚动区域
        self.cmd_canvas.configure(scrollregion=self.cmd_canvas.bbox("all"))

    def default_commands(self):
        for entry in self.command_entries:
            entry[0].destroy()
        self.cmd_canvas.configure(scrollregion=self.cmd_canvas.bbox("all"))
        self.selected_row = None
        self.add_default_commands()
        self.log("已恢复命令参数")
        pass

    def clear_all_commands(self):
        """清空所有命令内容，但保留结构"""
        if messagebox.askyesno("确认", "确定要清空所有命令内容吗？"):
            for entry in self.command_entries:
                entry[0].destroy()
            self.cmd_canvas.configure(scrollregion=self.cmd_canvas.bbox("all"))
            self.selected_row = None
            self.log("已清空所有命令内容")

    def log(self, message):
        """添加日志信息"""
        timestamp = time.strftime("%H:%M:%S")
        self.log_text.config(state=NORMAL)
        max_log_lines = int(self.log_line_clear.get())
        line_count = int(self.log_text.index('end-1c').split('.')[0])
        # 检查是否需要清理日志
        if line_count > max_log_lines:
            self.log_text.delete(1.0, f"{line_count}.0")

        self.log_text.insert(END, f"[{timestamp}]{message}\n")
        self.log_text.see(END)
        self.log_text.config(state=DISABLED)

    def log_clear(self):
        self.log_text.config(state=NORMAL)
        self.log_text.delete(1.0, END)
        self.log_text.config(state=DISABLED)

    def start_server(self):
        self.stop_server()
        """启动TCP服务器"""
        try:
            self.clients.clear()
            port = int(self.port_entry.get())
            if port < 1 or port > 65535:
                self.log("错误: 端口号必须在1-65535之间")
                return

            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.bind(('0.0.0.0', port))
            self.server_socket.listen(5)
            self.server_running = True

            self.start_btn.config(state=DISABLED)
            self.stop_btn.config(state=NORMAL)
            self.port_entry.config(state=DISABLED)

            self.log(f"服务器已启动，监听端口 {port}")

            # 启动服务器线程
            self.server_thread = threading.Thread(target=self.accept_connections, daemon=True)
            self.server_thread.start()

        except Exception as e:
            self.log(f"启动服务器失败: {str(e)}")

    def stop_server(self):
        """停止TCP服务器"""
        if self.server_running:
            self.server_running = False
            clients = list(self.clients.items())
            # 关闭所有客户端连接
            for addr, (client_socket, _) in clients:
                try:
                    client_socket.close()
                    self.log(f"已断开与 {addr} 的连接")
                except Exception as e:
                    self.log(f"关闭客户端 {addr} 失败: {str(e)}")
            # 关闭服务器socket
            if self.server_socket:
                try:
                    self.server_socket.close()
                    self.after(200, self.handle_stop_server)
                except Exception as e:
                    self.log(f"关闭服务器失败: {str(e)}")

    def update_ip_list_box(self):
        self.client_listbox.delete(0, END)
        for ip_port in self.clients:
            self.client_listbox.insert(END, ip_port)

    def handle_stop_server(self):
        # 清除设备信息
        self.clients.clear()
        self.tcp_device_ids.clear()
        self.client_listbox.delete(0, END)
        self.selected_client = None
        self.device_id_var.set("")
        self.start_btn.config(state=NORMAL)
        self.stop_btn.config(state=DISABLED)
        self.port_entry.config(state=NORMAL)
        self.server_running = False
        self.log("服务器已停止")

    def accept_connections(self):
        """接受客户端连接"""
        while self.server_running:
            try:
                client_socket, client_address = self.server_socket.accept()
                client_addr_str = f"{client_address[0]}:{client_address[1]}"

                self.clients[client_addr_str] = (client_socket, client_addr_str)
                self.tcp_device_ids[client_addr_str] = client_addr_str

                self.log(f"新客户端连接: {client_addr_str}")

                # 更新客户端列表
                self.client_listbox.insert(END, client_addr_str)

                # 启动线程处理客户端消息
                client_thread = threading.Thread(
                    target=self.handle_client,
                    args=(client_socket, client_addr_str),
                    daemon=True
                )
                client_thread.start()

            except Exception as e:
                if self.server_running:
                    self.log(f"接受连接时出错: {str(e)}")
                break

    def handle_client(self, client_socket, client_addr):
        """处理客户端通信"""
        try:
            while self.server_running:
                data = client_socket.recv(1024)
                if not data:
                    break
                message = data.decode('utf-8', errors='replace')
                log_filter = self.log_filter_var.get().strip()
                if len(log_filter) > 0:
                    if log_filter in data.hex().upper() or log_filter in message:
                        self.log(f"{client_addr} 收到: {data.hex().upper()}")
                        self.log(f"{client_addr} 收到: {message}")
                else:
                    self.log(f"{client_addr} 收到: {data.hex().upper()}")
                    self.log(f"{client_addr} 收到: {message}")
                # 识别设备ID
                if len(data) == 12 and self.to_find_device_id:
                    device_id = f"{data[5:9].hex().upper()}"
                    if not device_id in self.device_ids:
                        self.device_ids.append(device_id)
                        self.device_id_combobox["values"] = self.device_ids
                        self.device_id_combobox.current(0)
                    self.log(f" 搜索到ID: {device_id}")
                    self.to_find_device_id = False
                # 自动回复确认收到
                # client_socket.sendall(f"已收到: {message}".encode('utf-8'))

        except Exception as e:
            self.log(f"与 {client_addr} 通信时出错: {str(e)}")
        finally:
            # 清理客户端连接
            if client_addr in self.clients:
                del self.clients[client_addr]
                del self.tcp_device_ids[client_addr]

                # 更新客户端列表
                self.update_ip_list_box()
                self.log(f"客户端 {client_addr} 已断开连接")

                if self.selected_client == client_addr:
                    self.selected_client = None
            try:
                client_socket.close()
            except:
                pass

    def on_client_select(self, event):
        """处理客户端选择事件"""
        selection = self.client_listbox.curselection()
        if selection:
            index = selection[0]
            self.selected_client = self.client_listbox.get(index)
        else:
            self.selected_client = None

    def update_device_id(self):
        """更新设备ID 需要查询 设备ID依据选中的IP"""
        self.to_find_device_id = True
        if not self.clients:
            self.log("设备未连接")
            return
        if not self.selected_client:
            self.log("请选择IP查询")
            return
        client_socket, _ = self.clients[self.selected_client]
        command = "6804008109ffffffff5eaf16"
        if client_socket:
            client_socket.sendall(bytes.fromhex(command))

    def set_device_id(self):
        pass

    def clear_device_id(self):
        self.device_ids = []
        self.device_id_combobox["values"] = self.device_ids
        self.device_id_var.set("")

    def send_command(self, cmd_entry, name_entry, selected):
        """发送命令到选中的客户端，不清除命令内容"""
        if not self.server_running:
            self.log("服务器未运行，请先启动服务")
            return

        if not self.selected_client:
            self.log("请先选择一个客户端")
            return

        command = cmd_entry.get().strip()
        if not command:
            self.log("命令不能为空")
            return
        elif not selected:
            command = f"{command}\r\n\r\n"
        # 获取命令名称
        command_name = name_entry.get().strip() or "未命名命令"

        try:
            client_socket, _ = self.clients[self.selected_client]
            device_id = self.device_id_var.get().strip()
            if len(device_id) <= 0:
                self.log(f"请选择设备ID或者先获取设备ID")
                return
                # 转换ID为
            if isinstance(device_id, str):
                device_id = int(device_id, 16)
            command = construct_protocol(int(device_id).to_bytes(4, byteorder='big'), bytes(command, 'utf-8'))
            client_socket.sendall(bytes.fromhex(command))
            self.log(f"发送[{command_name}]到 {self.selected_client}: {command}")
        except Exception as e:
            self.log(f"发送命令失败: {str(e)}")

    def on_close(self):
        """窗口关闭时的处理，确保配置被保存"""
        # 停止服务器
        if self.server_running:
            self.stop_server()

        # 销毁窗口
        self.destroy()

    def create_widgets(self):
        pass
